//
//  MACD.cpp
//  Chart
//
//  Created by ma xiaoyang on 6/18/13.
//
//

#include "MACD.h"
#include "Rectangle.h"
#include "YTXChartStyle.h"

#include <vector>
#include <string>
#include <cmath>

using namespace ytx;
using namespace std;

MACD::MACD(IndexConfig indexConfig):IndexData(indexConfig, 2), penultimateAX(numeric_limits<double>::quiet_NaN()), penultimateBX(numeric_limits<double>::quiet_NaN()){
    type = QuotationType::MACD;
    indexName = "MACD";
    lineData.push_back(vector<double>());
    lineData.push_back(vector<double>());
    indexData.push_back(vector<double>());
    this->setColumn(Rect(2,2,2));
}

MACD::~MACD()
{

}

double MACDFn(double a, double b)
{
    return (a-b)*2;
}

void MACD::updateLastest(Quotation * data)
{
    if( !std::isnan(penultimateAX) && !std::isnan(penultimateBX) ){
        int len = getDataLength();

        int n = para.value[0];
        int n1 = para.value[1];
        int m = para.value[2];

        n = (int) Tools::between(para.range[0].first, para.range[0].second, n);
        n1 = (int) Tools::between(para.range[1].first, para.range[1].second, n1);
        m = (int) Tools::between(para.range[2].first, para.range[2].second, m);

        double AX, BX, DIF, preDEA, DEA, MACD;
        EmaFormula emaFormulaN(n);
        EmaFormula emaFormulaM(m);
        //收盘就是DI
        AX = emaFormulaN(data->kLineData.back().close, penultimateAX);
        BX = emaFormulaM(data->kLineData.back().close, penultimateBX);
        DIF = AX-BX;

        EmaFormula emaFormulaN1(n1);
        //倒数第二个DEA
        preDEA = getDataLength() > 1 ? lineData[0][len-2]: DIF;
        DEA = emaFormulaN1(DIF, preDEA);

        MACD = MACDFn(DIF, DEA);

        indexData[0].back() = MACD;
        lineData[0].back() = DEA;
        lineData[1].back() = DIF;
    }
}

void MACD::calculate(Quotation * data)
{
    lineData.clear();
    indexData.clear();

    int n = para.value[0];
    int n1 = para.value[1];
    int m = para.value[2];
    
    n = (int) Tools::between(para.range[0].first, para.range[0].second, n);
    n1 = (int) Tools::between(para.range[1].first, para.range[1].second, n1);
    m = (int) Tools::between(para.range[2].first, para.range[2].second, m);

    // prepare basic data
    const vector<KLineItem> & kLineData = data->kLineData;
    if (kLineData.size() <= 0) return;
    
    vector<double> DI(kLineData.size());
    std::transform(kLineData.begin(), kLineData.end(), DI.begin(), std::mem_fun_ref(&KLineItem::getClose));

    // short/long period EMA
    vector<double> AX(DI.size()), BX(DI.size());
    computeEMA(DI.begin(), DI.end(), n, AX.begin());
    computeEMA(DI.begin(), DI.end(), m, BX.begin());

    std::minus<double> diffFn;

    // MACD Line: (short period EMA - long period EMA)
    // [our] DIF: AX - BX
    vector<double> DIF(AX.size());
    std::transform(AX.begin(), AX.end(), BX.begin(), DIF.begin(), diffFn);
    penultimateAX = numeric_limits<double>::quiet_NaN();
    penultimateBX = numeric_limits<double>::quiet_NaN();
    if(DI.size() > 1){
        penultimateAX = (AX[AX.size()-2]);
        penultimateBX = (BX[BX.size()-2]);
    }
    else if(DI.size() == 1){
        penultimateAX = DI[0];
        penultimateBX = DI[0];
    }

    // Signal Line: 9-day EMA of MACD Line
    // [our] DEA: 9-day EMA of DIF
    vector<double> DEA(DIF.size());
    computeEMA(DIF.begin(), DIF.end(), n1, DEA.begin());

    // MACD Histogram: MACD Line - Signal Line
    // [our] MACD: DIF - DEA
    vector<double> MACD(DIF.size());
    std::transform(DIF.begin(), DIF.end(), DEA.begin(), MACD.begin(), MACDFn);
    
    indexData.push_back(MACD);
    lineData.push_back(DEA);
    lineData.push_back(DIF);
    initInfo(data);
}


bool MACD::hasData() const
{
    return (lineData.size() > 0 && lineData[0].size() > 0);
}

bool MACD::hasData(int index) const
{

    if (hasData())
    {
        int count = (int)lineData[0].size();
        return 0 <= index && index < count;
    }
    else{
        return false;
    }
}


string MACD::getCrosshairYData(int index)
{
    return "";
}


pair<double, double> MACD::getAdjustedDataMinMax(int startIndex, int endIndex, const Rectangle & rect) const
{
    pair<double, double> indexMinMax = getIndexDataMinMax(startIndex, endIndex);
    double a = std::max(std::abs(indexMinMax.first), indexMinMax.second);

    pair<double, double> lineDataMinMax = getLineDataMinMax(startIndex, endIndex);
    double b = std::max(std::abs(lineDataMinMax.first), lineDataMinMax.second);

    a = std::max(a, b);
    a *= 1.05;

    return make_pair(-a, a);
}

std::vector<std::pair<double, std::string> > MACD::getLeftSideText(int startIndex, int endIndex, const Rectangle & rect, const Style & style) const
{
    pair<double, double> minmax = getIndexDataMinMax(startIndex, endIndex);
    vector<pair<double, string> > ret;
    ret.push_back(make_pair(minmax.first, style.sideTextColor));
    ret.push_back(make_pair(numeric_limits<double>::quiet_NaN(), style.sideTextColor));
    ret.push_back(make_pair(minmax.second, style.sideTextColor));

    return ret;
}


std::vector<std::pair<std::string, std::string> > MACD::getTopSideText(const std::vector<std::vector<double> > & data, int index) const
{
    vector<pair<string, string> > ret;

    if(hasData(index)){
        string s1 = std::isnan(lineData[0][index]) ? "--" : Tools::Convert2FixString(lineData[0][index]);
        string s2 = std::isnan(lineData[1][index]) ? "--" : Tools::Convert2FixString(lineData[1][index]);
        string s3 = std::isnan(indexData[0][index]) ? "--" : Tools::Convert2FixString(indexData[0][index]);
        ret.push_back(make_pair(titleType[1] + ':' + s1, titleType[0]));
        ret.push_back(make_pair(titleType[3] + ':' + s2, titleType[2]));
        ret.push_back(make_pair(titleType[5] + ':' + s3, titleType[4]));
    }

    return ret;
}
